---
title: "Verify a W3C Verifiable Presentation envelope"
method: POST
path: "/credentials/verify"
tags: ["federation"]
---

# Verify a W3C Verifiable Presentation envelope

`POST /credentials/verify`

## Request

- Base URL: `https://pact0.com/api/v1`
- URL: `https://pact0.com/api/v1/credentials/verify`
- Auth: none required

ALIP-0011 federation surface. Caller posts a JSON VP envelope
(an `application/vc+ld+json` document); the substrate verifies
the eddsa-jcs-2022 signature against the issuer's published
JWKS and returns a structured verification result.

Same surface on the MCP `verify_credential` tool. Anonymous-
allowed (the verification is pure cryptography; no caller
state mutated).

**Scope: signatures only.** The hosted verifier performs NO
revocation check — after a `valid: true`, checking the
credential's `claim_id` against the list at the VP's
`revocations_url` (`/.well-known/credential-revocations.json`)
is the caller's responsibility.

Rate-limited per client IP (each call drives an outbound JWKS
fetch): exceeding the bucket returns `429 rate_limited` with
`Retry-After`. The fetched JWKS body is size-capped.

## Request body

- object
  - `credential` object, required: The W3C Verifiable Presentation (or Verifiable Credential) JSON-LD document to verify, passed VERBATIM (do not trim or paraphrase — that changes the JCS canonical form and the signature reads invalid). Prefer `/credentials/verify-url` for large bodies / lossy JSON pipes.
  - `jwks_url` string, uri: OPTIONAL (ALIP-0033). When omitted, the substrate derives the issuer JWKS URL from `proof.verificationMethod`. Supply it only to pin a key set; a value that disagrees with the proof-derived URL is rejected (confused-deputy guard).

## Response `200`

OK — verification result (may be `valid:false` if signature fails)

- object
  - `success` true
  - `data` object: The verification result. `errors` is empty when valid=true.
    - `valid` boolean
    - `details` object[]: Per-credential verification result.
      - `credential_index` integer
      - `credential_type` string[]
      - `valid` boolean
      - `kid` string
      - `reason` string: When valid=false
    - `errors` string[]: Errors aggregated across all checks (empty when valid=true).
    - `jwks_url` string: The JWKS source the verifier resolved keys from.
    - `jwks_kids` string[]: The kids advertised by that JWKS.

## Other responses

- `400`: invalid_vp (malformed JSON / not a VP / missing proof block)
- `401`: `bearer_not_accepted` — an Authorization header was sent to this public, unauthenticated endpoint; drop it.
- `422`: ALIP-0033 trust-anchor derivation failures: `malformed_issuer` (`proof.verificationMethod` isn't a valid did:web identifier), `missing_proof` (no proof block / verificationMethod), `unsupported_did_method` (a non-did:web method; only did:web verifies at M2.5), or `jwks_issuer_mismatch` (a caller-supplied `jwks_url` disagrees with the proof-derived one).

---

[API](https://skmtc.dev/pact0/apis/pact0-api.md) · [All operations](https://skmtc.dev/pact0/apis/pact0-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pact0/apis/pact0-api/revisions/e48bb10d300f?raw)
