Skip to main content

Developer Guide

How to integrate OpenProdkt Governance into an application or agent.

If you want to make your first call, start with the Quickstart. This page covers the concepts and behaviors you need once you are building for real.

The integration model​

Governance is a pre-flight check. Your application asks before it acts.

The product never executes your action for you. It answers a question and records the answer.

Core concepts​

ConceptWhat it is
AssetA thing you govern — an AI agent, model endpoint, API, or database
Asset keyStable identifier your code sends at runtime, for example billing.refund-agent
GroupA set of approvers responsible for reviewing an asset
PolicyA rule attached to an asset that produces an outcome
DecisionOne evaluation, with a permanent ID
ApprovalA human review task created by an approval_required decision
Policy revisionAn immutable snapshot of a policy at a point in time

Authentication​

There are two credential types and they are deliberately separate.

CredentialUsed byObtained via
JWTHumans in the ConsoleEmail one-time code
API keyApplications and agentsCreated in the Console

Both are sent the same way:

Authorization: Bearer <jwt-or-api-key>

Never put an API key in a browser, mobile app, or any client you do not control. API keys are server-side credentials.

Scopes​

Every API key carries scopes. Grant the minimum your integration needs — a service that only evaluates decisions needs decision:run and nothing more. A request missing the required scope returns 403.

Identifying the asset​

An evaluation request identifies its asset by either asset.id or asset.key — exactly one, never both. A keyed request also sends application, service, and environment, each a lowercase DNS label.

{
"version": "v1",
"asset": { "id": "ast_e8f1a2c3" },
"operation_id": "refund-2026-001",
"transport": { "kind": "http" },
"metadata": { "amount": 9000, "currency": "USD" }
}

Prefer asset.key for readable source code. Conflicting identity fields return 409 asset_identity_conflict.

All context belongs inside metadata. Top-level action, amount, and workflow fields are rejected.

Unknown assets are surfaced, not blocked​

If you send an asset key that is not registered, the result is allow with reason asset_not_registered. The key is recorded as a discovery observation for an administrator to review and promote into a governed asset.

This is intentional: an unrecognized tool is never silently blocked, but it is always surfaced for review. See Detection and Decision Flow for the full picture.

Interpreting outcomes​

OutcomeMeaningCorrect handling
allowPermittedProceed
approval_requiredNeeds human review. SDKs receive HTTP 202; the proxy receives HTTP 403 with a challengePause and wait; do not proceed. Retry the same operation_id after approval
denyBlockedStop and surface the reason

The most common integration bug is treating approval_required as either success or failure. It is neither — it means not yet.

An asset with no matching policy defaults to allow with reason no_matching_policy. Governance is opt-in: define a policy for any workflow you want restricted or reviewed.

Errors​

StatusMeaningAction
400Invalid requestFix the payload; do not retry unchanged
401Missing or invalid credentialCheck the key; do not retry
403Missing scope or insufficient role, or a denied or terminal approvalGrant the scope or stop; do not retry unchanged
403 with code=approval_required (proxy)Approval pendingWait for approval (see Approvals); retry after retry_after_seconds
404Resource not foundVerify the ID
409State conflict, such as approving twiceRe-read current state
409asset_identity_conflict, operation_id_conflict, or approval_retry_mismatchDo not retry unchanged; send the original context or use a new operation_id
409operation_in_progressRetry after a short backoff
402Decision, approval, or notification quota exhaustedCheck /v1/usage and the active plan
429Rate limit exceededBack off and retry
503Policy evaluation failed; the request fails closed (policy_evaluation_failed)Retry with backoff
5xxServer errorRetry with backoff

Only 429, 5xx, and 409 operation_in_progress are worth retrying. A 402 quota response is terminal until the quota period resets or the plan changes. Use exponential backoff with jitter for retryable responses, and cap the attempts.

Idempotency​

Evaluations of assets with one or more assigned policies are recorded and consume one decision unit. Approval-required operations are idempotent by operation_id: repeating the same request returns the existing approval and adds no usage. Unknown assets are pass-through: they create no decision record and are not billed. Registered assets with no assigned policies are allowed with reason no_policies_assigned, are recorded in decision history, and are not billed. Each recorded evaluation consumes quota, so do not repeat a successful call.

Approve and reject are state transitions. Approving an already-approved item returns a conflict rather than double-approving.

For discovery ingest, the discovery_id you supply is the idempotency key. Repeated observations aggregate instead of duplicating.

Operation IDs​

Every evaluation requires an operation_id that identifies one business operation, such as a refund ID or an idempotency key. Use letters, digits, ., _, :, and -, up to 128 characters, so the same ID works through the SDKs and the transparent proxy. Reusing an ID with a changed authorization context returns 409 operation_id_conflict. An approved retry must repeat the same operation_id, asset, transport, and metadata.

Pagination​

List endpoints accept limit and offset, and return total alongside the results.

GET /v1/assets?limit=25&offset=0

limit accepts 1–100 and defaults to 25. Page by advancing offset until you have consumed total.

Most list endpoints also accept search for case-insensitive partial matching.

Rate limits and quota​

Requests are subject to per-key throttling and separate monthly decision, approval, and notification quotas. Rate-limit exhaustion returns 429; a monthly usage quota returns 402 with a machine-readable code.

Two things worth knowing:

  • The Playground consumes quota. It calls the real decision API.
  • Health and documentation endpoints do not. Only billable actions count.

Read current consumption from the usage endpoint rather than tracking it yourself.

GET /v1/usage reports decisionRequests, approvalRequests, and notificationRequests, together with matching monthly limits under limits. A quota response uses one of decision_quota_exceeded, approval_quota_exceeded, or notification_quota_exceeded so clients can identify which capacity is exhausted.

Usage units​

OpenProdkt meters policy execution separately from approvals and notifications:

ActivityUnits
Asset discovery or inventory ingestion0
/v1/evaluate: unknown asset pass-through0
/v1/evaluate: registered asset with no policies (recorded in decision history)0
Evaluation of an asset with assigned policies1 decision unit, even when no condition matches
Playground evaluation of a registered asset1 decision unit, including no-policy or no-match tests
Playground pass-through for an unknown asset key0
Creation of a pending approval1 approval unit, in addition to its decision unit
Duplicate request for the same pending operation0 additional units
Approval status polling or resolution0
Approved approval redemption/retry0
PDP-created approval lifecycle notification request1 notification unit per request
Delivery retries, queue redelivery, or duplicate webhook handling0 additional units

An approval lifecycle notification is counted when OpenProdkt creates the outbound request, not when a receiver acknowledges it. Retrying delivery does not create another unit. The usage response reports all three counters and their active monthly limits.

Environments​

The sandbox is available at https://api.sandbox.openprodkt.com. Make the base URL configurable rather than hardcoding it, so the same build can target a different environment.

Testing your integration​

  1. Register an asset with a representative definition.
  2. Add an allow policy for the normal case and an approval_required policy for the sensitive case.
  3. Use the Playground with Include evaluation trace enabled to confirm which policy wins and why.
  4. Exercise all three outcomes in your code path before shipping.
  5. Confirm each decision appears in Decision Explorer with the expected policy revision.

The evaluation trace is the fastest way to debug an unexpected outcome. It shows every policy considered, in order, and why each matched or did not.

Policy evaluation order​

Understanding this prevents most "wrong outcome" surprises:

  1. Load policies attached to the asset.
  2. Evaluate newest first.
  3. First match wins — evaluation stops immediately.
  4. If nothing matches, deny.

A broad new policy can shadow a narrower older one. When an outcome surprises you, check the trace before changing rules.

Next steps​