---
title: "Validate proof token"
method: POST
path: "/api/v1/proofs/validate"
tags: ["Proofs"]
---

# Validate proof token

`POST /api/v1/proofs/validate`

## Request

- Base URL: `https://api.proof.holdings`
- URL: `https://api.proof.holdings/api/v1/proofs/validate`
- Auth: none required

Validate a JWT proof token. Public endpoint — no authentication required. Can also be done offline using JWKS.

## Request body

- ValidateProofRequest: Request body for validating a proof token
  - `proof_token` string, required: JWT proof token to validate
  - `identifier` string: Optional binding check: the identifier you expect this proof to be about. When supplied, the proof is only valid if it hashes to the token's signed identifier_hash — a mismatch returns valid:false with reason "identifier_mismatch", and a proof whose hash is derived from ciphertext returns "identifier_unverifiable". For a proof about a verified identifier (phone, email, domain, social, wallet, telegram_bot) any format is accepted and canonicalized as it was at issuance; a proof of an approval decision binds only on the exact recipient value it was issued for.

## Response `200`

Validation result

- ValidateProofResponse: Proof validation result. Returns verification details when valid, or reason/message when invalid. A DELEGATION token answers with `token_type: "delegation"` and the `delegation` block instead of `verification`; its status resolves the cascade down to the domain control proof, which neither the status list (it packs each row's own bit) nor an offline signature check can show. Delegation-specific reasons: `unknown_delegation` (no registry record — delegations are minted fail-closed, so this is invalid, not merely unresolvable), `status_unavailable`, and `identifier_unverifiable` when an `identifier` binding is requested for a delegation.
  - `valid` boolean, required
  - `proof_id` string: Public proof id (ph_ctl_*), present when the proof is registered
  - `verification` object
    - `id` string, required: The token's subject claim — a verification id for most proofs, a prefixed subject (e.g. domain_<id>) for asset proofs
    - `type` 'phone' | 'email' | 'domain' | 'social' | 'wallet' | 'account' | 'telegram_bot' | 'confirmation' | 'authorization', required: What a proof token proves: control of an asset (the verification types), an approved or denied confirmation, or a granted or refused authorization
    - `channel` 'whatsapp' | 'telegram' | 'sms' | 'email' | 'dns' | 'http' | 'auto' | 'github' | 'google' | 'facebook' | 'x' | 'linkedin' | 'instagram' | 'youtube' | 'tiktok' | 'ethereum' | 'solana' | 'bitcoin' | 'coinbase' | 'kraken' | 'telegram_bot_token' | 'browser', required: The channel the proof was obtained over — a verification channel, or `browser` for a confirmation approved on the approval page
    - `identifier_hash` string, required: SHA-256 hash of the verified identifier — compare with your own SHA-256 hash to verify ownership
    - `decision` 'approve' | 'deny': The decision the proof records — present on `confirmation` and `authorization` proofs only
    - `verified_at` string, date-time, required
    - `expires_at` string, date-time, required
  - `reason` string: Failure reason: expired, revoked, suspended (a reversible pause — the owner can lift it), invalid, identifier_mismatch (the supplied identifier is not what this proof is about), or identifier_unverifiable (the proof's hash is derived from ciphertext, so no identifier can be bound to it)
  - `suspended_at` string, date-time: When the suspension began — present only with reason `suspended`
  - `message` string: Human-readable message
  - `revoked_at` string, date-time: When the proof was revoked (only present when reason is "revoked")
  - `revoked_reason` string: Reason for revocation (only present when reason is "revoked")
  - `token_type` 'delegation': Present ONLY when the submitted token is a delegation (m-delegation-verifier-ref). Its absence means the answer is about a verification/decision proof and the `verification` block applies.
  - `delegation` object: Delegation claim set, present when a valid delegation token was submitted. The attestation is domain authorization of the artifact — NOT that the artifact is safe, audited or endorsed.
    - `id` string, required: Public delegation handle (ph_dlg_*)
    - `principal` string, required: The domain whose controller authorized the artifact
    - `delegate` string, required: Canonical artifact identifier. Compare this byte-for-byte with the artifact identity you resolved INDEPENDENTLY — a published token is a bearer artifact, so comparing it against a field of the same card proves nothing.
    - `scope` string[], required
    - `control_proof` string, required: Public handle of the domain control proof this delegation rests on
    - `issued_at` string, date-time, required
    - `expires_at` string, date-time, required

## Other responses

- `400`: Invalid request

---

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