DocsIntegrationsPCA API Reference

PCA API Reference

REST endpoint reference for issuing, listing, fetching, revoking, and verifying Policy Commitment Attestation credentials. Plus the three public did:web and Status List 2021 endpoints used by external verifiers.

This page documents every endpoint in the Policy Commitment Attestation (PCA) surface. For the conceptual overview, see PCA Overview. For who-are-PCA-agents-anyway and how to register them, see Agent Identity for Real Teams.

Authentication

All endpoints under /api/agent/attestations/* require an API key passed as Authorization: Bearer dv_live_…. The key must have the appropriate pca:* permission for the action (see the table at the end of each endpoint section).

The three public endpoints (/tenants/[id]/did.json, /agents/[slug]/did.json, /api/governance/status-lists/[id]) are anonymous — required by the W3C did:web and Status List 2021 specs to be publicly accessible.

POST /api/agent/attestations

Issue a new signed PolicyCommitmentCredential. Requires pca:write.

Request

{
  "subjectDid": "did:web:dictiva.com:agents:code-reviewer",
  "statementId": "550e8400-e29b-41d4-a716-446655440000",
  "statementRef": {
    "uri": "https://dictiva.com/statements/STMT-AI-001",
    "version": 1,
    "hash": "sha256:b8a1d2c3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1"
  },
  "commitmentTier": "T4",
  "targetTier": "T5",
  "scope": {
    "tenants": ["dictiva"],
    "entityTypes": ["process", "assembly"],
    "actionClasses": ["read", "propose_edit"],
    "refusalRules": []
  },
  "evidence": [
    { "evidenceKind": "adr",                       "reference": "ADR-040" },
    { "evidenceKind": "memory_file",               "reference": "memory/pca.md" },
    { "evidenceKind": "semantic_similarity_report","reference": "https://example.com/reports/r1.json", "digest": { "sha256": "sha256:9f8e..." } }
  ],
  "chainOfAuthority": {
    "issuedBy": "did:web:dictiva.com:users:alice",
    "approvedBy": ["did:web:dictiva.com:users:bob"]
  },
  "revalidationInterval": "P90D"
}

Required fields

FieldTypeNotes
subjectDiddid:web: URIMust reference an existing agent_identities row in your tenant.
statementIdUUIDLocal statement UUID. Resolve via search_statements MCP tool or GET /api/agent/statements.
statementRef.uriURLDisplay URL — what the credential exposes. Free-form.
statementRef.versioninteger ≥ 1Pinned version.
statementRef.hashsha256:<hex>Canonical hash of the statement body. The issuer recomputes against the current body and rejects on drift (statement_hash_mismatch).
commitmentTierT1T6Claimed maturity. Cumulative floors apply.
evidenceEvidenceRef[]Must satisfy cumulative floors for the claimed tier (see Maturity Ladder).
chainOfAuthority.issuedBydid:web: URIHuman or system that authorized the issuance.

Optional fields

FieldDefault
targetTier(none)
scope{} (T5+ requires non-empty)
revalidationIntervalP90D (ISO-8601 duration)
chainOfAuthority.approvedBy(none)

Response — 201 Created

The full attestation row including the signed VC. Key fields:

{
  "id": "uuid",
  "tenantId": "uuid",
  "agentIdentityId": "uuid",
  "subjectDid": "did:web:...",
  "statementId": "uuid",
  "statementVersion": 1,
  "statementHash": "sha256:...",
  "commitmentTier": "T4",
  "credential": { /* full signed W3C VC */ },
  "issuedAt": "2026-04-25T...",
  "expiresAt": "2026-07-24T...",
  "revalidationInterval": "P90D",
  "signatureAlgorithm": "ed25519",
  "statusListUrl": "https://dictiva.com/api/governance/status-lists/...",
  "statusListIndex": 42,
  "evidence": [ /* per-row evidence */ ]
}

Errors

CodeHTTPCause
invalid_body400Zod validation failure — see details array.
agent_not_found404subjectDid not in this tenant's agent_identities.
statement_not_found404statementId doesn't exist or is soft-deleted.
statement_version_not_found404The version doesn't exist for that statement.
statement_hash_mismatch422Supplied hash doesn't match canonical hash at this version (drift).
Tier floor not satisfied500Evidence doesn't satisfy cumulative floors. (Caller should validate first.)

GET /api/agent/attestations

Paginated list. Requires pca:read.

Query params

ParamTypeDefault
subject_didstring— (filter)
statement_idUUID— (filter)
tierT1T6— (filter)
include_revokedtrue / falsefalse
pageint ≥ 11
per_pageint 1–10020

Response — 200

{
  "data": [ /* PolicyCommitmentAttestation[] */ ],
  "pagination": {
    "page": 1,
    "perPage": 20,
    "total": 137,
    "totalPages": 7
  }
}

GET /api/agent/attestations/:id

Single attestation by ID. Requires pca:read. Returns the same shape as a single element of the list response.

Errors

CodeHTTPCause
invalid_id400:id path parameter isn't a UUID.
attestation_not_found404ID unknown, in a different tenant, or soft-deleted.

POST /api/agent/attestations/:id/revoke

Revoke. Requires pca:revoke — highest-privilege action; only Owner / Admin / Policy Owner roles have it by default.

Request

{ "reason": "Agent decommissioned; credentials no longer authoritative." }

Response — 200

The updated attestation row, now showing:

  • revokedAt — server timestamp
  • revokedReason — the reason provided
  • credential.credentialStatus still references the same status list URL + index, but the bit at that index is now flipped to 1

Errors

CodeHTTPCause
attestation_not_found404Same as GET /:id.
attestation_already_revoked409revoked_at is already set. Idempotency by design.

POST /api/agent/attestations/verify

Stateless verification. Requires pca:read. Always returns HTTP 200 — the verified boolean in the response body is the signal.

Request

{ "credential": { /* signed W3C VC */ } }

Response — 200

{
  "verified": true,
  "claimedTier": "T4",
  "effectiveTier": "T4",
  "revocationStatus": "active",
  "withinValidityWindow": true,
  "evidenceChecks": [
    { "evidenceIndex": 0, "evidenceKind": "adr",          "reference": "ADR-040", "expected": null,        "actual": null,        "resolved": false, "matched": true },
    { "evidenceIndex": 1, "evidenceKind": "memory_file",  "reference": "memory/pca.md", "expected": null,  "actual": null,        "resolved": false, "matched": true },
    { "evidenceIndex": 2, "evidenceKind": "semantic_similarity_report", "reference": "https://example.com/r1.json", "expected": "sha256:9f...", "actual": "sha256:9f...", "resolved": true, "matched": true }
  ]
}

Five checks executed

  1. Issuer resolution — credential's issuer resolved via tenant's tenant_signing_keys. Failure → issuer_unknown.
  2. Cryptographic verification — VC Data Integrity / Ed25519 via DigitalBazaar. Failure → cryptographic_verification_failed.
  3. Validity windowvalidFrom ≤ now ≤ validUntil. Failure → outside_validity_window.
  4. Revocation lookup — Status List 2021 bit (preferred) → soft revoked_at column (fallback). Failure → credential_revoked.
  5. Evidence digest resolution — for each evidence entry with a digest.sha256 claim, fetch the artifact (HTTPS only) and recompute SHA-256. Drift → evidence_digest_mismatch.

revocationStatus semantics

ValueMeaning
activeCredential is in our DB or its status list bit is 0.
revokedStatus list bit is 1, OR the credential is in our DB with revoked_at set.
unknownCredential isn't in our DB and has no credentialStatus field (legacy / external issuer). The verifier can't determine revocation state.

When revocationStatus === "unknown", the credential may still verify (verified: true) if the signature + window check pass — but operators should treat it as lower confidence than active.

Public endpoints (no auth)

GET /tenants/[id]/did.json

Returns the tenant's W3C DID document, used to verify any credential issued by that tenant. id must be a UUID.

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/ed25519-2020/v1"
  ],
  "id": "did:web:dictiva.com:tenants:550e8400-e29b-41d4-a716-446655440000",
  "verificationMethod": [
    {
      "id": "did:web:dictiva.com:tenants:550e8400-...#key-1",
      "type": "Ed25519VerificationKey2020",
      "controller": "did:web:dictiva.com:tenants:550e8400-...",
      "publicKeyMultibase": "z6Mk..."
    }
  ],
  "assertionMethod": ["did:web:dictiva.com:tenants:550e8400-...#key-1"]
}

Headers: Content-Type: application/did+json, Cache-Control: public, max-age=300.

GET /agents/[slug]/did.json

Returns the agent's W3C DID document. Optional verificationMethod array — populated only when the agent self-signs (most agents are subjects only and have no key material).

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/ed25519-2020/v1"
  ],
  "id": "did:web:dictiva.com:agents:code-reviewer",
  "alsoKnownAs": ["mailto:code-reviewer@agents.dictiva.com"],
  "service": [
    {
      "id": "did:web:dictiva.com:agents:code-reviewer#dictiva-workbench",
      "type": "DictivaWorkbench",
      "serviceEndpoint": "https://dictiva.com/workbench/teams/code-reviewer"
    }
  ],
  "selfDescribedRole": "PR code quality, security, conventions",
  "model": "claude-opus-4-7"
}

GET /api/governance/status-lists/[id]

Returns the signed Status List 2021 credential. Verifiers fetch this to check whether a specific bit (the credential's statusListIndex) is flipped to 1 (revoked) or remains 0 (active).

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://w3id.org/vc/status-list/2021/v1"
  ],
  "id": "https://dictiva.com/api/governance/status-lists/abc...",
  "type": ["VerifiableCredential", "StatusList2021Credential"],
  "issuer": "did:web:dictiva.com:tenants:...",
  "validFrom": "2026-04-25T...",
  "credentialSubject": {
    "id": "https://dictiva.com/api/governance/status-lists/abc...#list",
    "type": "StatusList2021",
    "statusPurpose": "revocation",
    "encodedList": "H4sIAAAAAAAA…"
  },
  "proof": {
    "type": "Ed25519Signature2020",
    "created": "...",
    "verificationMethod": "...",
    "proofPurpose": "assertionMethod",
    "proofValue": "z..."
  }
}

The encodedList is a 131,072-bit (16 KiB) bitstring, gzip-compressed and base64url-encoded. Verifiers decode, locate their bit at statusListIndex, and check whether it's set.

Headers: Content-Type: application/ld+json, Cache-Control: public, max-age=60.

Rate limits

Per-tenant, fixed-window 60s, Redis-backed:

PlanRequests/min
Community5
Professional20
Business100
Enterprise500

The MCP Governance Server (which wraps these endpoints) requires Business+ entitlement. See the MCP Governance Server Guide.

OpenAPI

The full machine-readable spec is at GET /api/openapi.json. Interactive Scalar UI at GET /api/docs.

See also