Skip to content

PDP Adjudication API ​

The Policy Decision Point (PDP) evaluates content against active constitutions and returns a decision kernel -- the verdict, rationale, and which norms applied.

POST /api/v1/pdp/adjudicate ​

Submit a message for policy adjudication.

Request ​

bash
curl -X POST "https://api.creed.space/api/v1/pdp/adjudicate" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "How do I reset my password?",
    "persona": "ambassador",
    "constitutions": ["UEF"],
    "adherence_level": 3,
    "influence_scope": "advise_only"
  }'

Request Body ​

FieldTypeRequiredDefaultDescription
messagestringYes--Text to evaluate (max 32,000 chars)
personastringNoambassadorPersona context for evaluation
constitutionsstring[]No["UEF"]Constitution IDs to evaluate against (max 20)
adherence_levelintegerNo3Strictness from 1 (lenient) to 5 (strict)
influence_scopestringNoadvise_onlyOne of advise_only, compare_options, motivate_with_disclosure
contextobjectNonullAdditional context attributes
noncestringNonullClient-supplied nonce for verification (max 100 chars)
transparency_levelintegerNo00-3, controls detail in response

Response -- Decision Kernel ​

json
{
  "decision": "permit",
  "rationale": "Message is a routine support request with no safety concerns.",
  "norms": [
    {"t": "P", "act": "provide_support", "ref": "pdp"}
  ],
  "precedence": ["UEF"],
  "sources": [],
  "caveats": [],
  "nonce": "abc123",
  "hash": "sha256:...",
  "timestamp": 1716566400.0,
  "decision_id": "pdp_abc123"
}

Each norm has a type t: O (obligation: a required action), F (forbidden action) or P (permitted action). act names the action and ref names the source of the norm.

Decision Values ​

The kernel uses this public vocabulary:

DecisionMeaning
permitContent may proceed
divertContent should be altered or redirected to a safer alternative
forbidContent violates policy and must be withheld
dependsMore context or human review is needed

Internal decision types that have no direct public equivalent (for example transform, creed_conflict and partial_compliance) are reported as divert or depends. Treat any decision other than permit as not permitted.

Gate on permit, not on block

The kernel never returns allow, block, modify or escalate. Code that checks decision == "block" will never match and will let forbidden content through. Allow content only when decision == "permit", and withhold or review everything else.

Kernel Fields ​

FieldTypeDescription
decisionstringThe policy decision
rationalestringHuman-readable explanation (max 500 chars)
normsobject[]Constitutional norms that informed the decision
precedencestring[]Constitution priority order applied
sourcesobject[]Evidence sources
caveatsstring[]Qualifications or warnings
noncestringEcho of client nonce if provided
hashstringCryptographic hash of the kernel for verification
timestampfloatUnix timestamp of the decision
decision_idstring | nullStable ID for the decision dossier, override and appeal endpoints. Not part of the kernel hash

Verify a Decision ​

These endpoints need a signed-in user session (dashboard session cookie or login JWT), not an API key. A call with X-API-Key returns 401.

After receiving a kernel, verify that it was applied:

bash
# Check if kernel was applied
GET /api/v1/pdp/verify/{nonce}

# Confirm kernel application (called by AI after use)
POST /api/v1/pdp/verify/{nonce}/confirm

Batch Evaluation ​

Evaluate multiple actions in a single request (1-100 items). Each action consumes one rate-limit token.

bash
curl -X POST "https://api.creed.space/api/v1/pdp/batch-evaluate" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "actions": [
      {"action": "Send email to user", "context": "password reset"},
      {"action": "Delete all records", "context": "admin cleanup"}
    ],
    "constitution_ids": ["UEF"],
    "session_id": "sess_abc123"
  }'

Batch Response ​

json
{
  "results": [
    {
      "decision_type": "allow",
      "allowed": true,
      "confidence": 0.95,
      "reasons": ["..."],
      "reason_codes": [],
      "risk_score": 0.05,
      "decision_id": "pdp_abc123",
      "evaluation_time_ms": 12.4
    },
    {
      "decision_type": "block",
      "allowed": false,
      "confidence": 0.9,
      "reasons": ["..."],
      "reason_codes": ["..."],
      "risk_score": 0.92,
      "decision_id": "pdp_def456",
      "evaluation_time_ms": 15.1
    }
  ],
  "batch_id": "550e8400-e29b-41d4-a716-446655440000",
  "total": 2,
  "blocked_count": 1
}

Batch results report decision_type in the internal vocabulary (allow, block, modify, transform, escalate, creed_conflict, partial_compliance), not the public kernel vocabulary. Gate on the boolean allowed field, and treat allowed: false or a missing allowed as not permitted.

Appeal a Decision ​

Request re-evaluation of a previous decision with additional context:

bash
curl -X POST "https://api.creed.space/api/v1/pdp/appeal" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "decision_id": "pdp_abc123",
    "original_message": "The blocked message text",
    "appeal_reason": "This was a legitimate support question about..."
  }'

The server verifies that the decision belongs to the authenticated account, that original_message exactly matches the evaluated input, and that the decision is still inside its bounded appeal window. The original server-side constitution set is reused. Callers cannot substitute a different policy set.

An accepted appeal returns a short-lived, single-use authorization:

json
{
  "allowed": true,
  "decision": "allow",
  "reason": "Current policy permits an exact retry with verified context.",
  "appeal_authorization_token": "pdpapl1.<authorization-id>.<secret>",
  "appeal_authorization_expires_at": "2026-07-29T12:05:00Z",
  "source_thread_id": "01890f31-9bd2-7cc1-98c8-123456789abc"
}

As with batch results, the appeal decision uses the internal vocabulary; gate on allowed.

Retry the exact original message on source_thread_id through POST /api/v1/runs/stream. Send the returned token and the exact same appeal reason as top-level request fields:

json
{
  "input": {"content": "The blocked message text"},
  "configurable": {
    "thread_id": "01890f31-9bd2-7cc1-98c8-123456789abc",
    "runConfig": {}
  },
  "appeal_authorization_token": "pdpapl1.<authorization-id>.<secret>",
  "appeal_reason": "This was a legitimate support question about..."
}

The authorization is bound to the authenticated account, decision, thread, original input, appeal reason, and server constitution set. It is consumed atomically and cannot be replayed. The retry still runs the current PDP and Guardian policies; an accepted appeal is verified context for one evaluation, not a bypass. Appeal responses are marked Cache-Control: no-store.

Influence Ledger ​

Requires a signed-in user session (dashboard session cookie or login JWT), not an API key. A call with X-API-Key returns 401.

Track how PDP decisions influence AI behavior over time:

bash
GET /api/v1/pdp/influence/ledger?limit=50

Returns influence entries with the decision history for the authenticated account.

Error Responses ​

Only API-key authentication (401) and scope (403) errors follow RFC 7807 (application/problem+json); see Authentication. Branch on the HTTP status code, not the body.

422 Unprocessable Entity -- validation failure (for example message longer than 32,000 characters):

json
{
  "detail": [
    {"loc": ["body", "message"], "msg": "String should have at most 32000 characters", "type": "string_too_long"}
  ],
  "error": "Validation error"
}

429 Too Many Requests -- default 100 requests per 60 seconds per key:

json
{
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Please try again later.",
  "retry_after": 30
}

Wait for the number of seconds in the Retry-After header. The per-IP limit that runs before authentication returns a different body ({"error": {"type": "rate_limit_exceeded", ...}, "detail": "Rate limit exceeded"}), so read Retry-After rather than parsing the body.

500 Internal Server Error -- audit trail failure (strict mode):

json
{
  "error": "Audit trail recording failed; request aborted for safety",
  "status_code": 500
}

Fail-Closed

In strict audit mode (default), requests are rejected if the audit trail cannot be recorded -- no decision goes unlogged.

Creed Space API documentation. Privacy, Terms and Compliance live on creed.space.