---
title: "Revoke delegation"
method: POST
path: "/api/v1/delegations/{id}/revoke"
tags: ["Delegations"]
---

# Revoke delegation

`POST /api/v1/delegations/{id}/revoke`

## Request

- Base URL: `https://api.proof.holdings`
- URL: `https://api.proof.holdings/api/v1/delegations/{id}/revoke`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `auth_token`

Revoke ONE delegation. The domain proof beneath it and every sibling delegation stay valid — that independent lifecycle is why the delegation is a separate record. The revocation reaches the revocation list, the Token Status List and the proof status endpoint. Idempotent.

## Path parameters

- `id` string, required: Delegation Mongo id or public handle (`ph_dlg_<32 hex>`)

## Request body

- object
  - `reason` 'user_request' | 'fraud' | 'key_compromise' | 'other'

## Response `200`

Delegation revoked — and a repeat revoke answers 200 with the same body, on this address AND on `POST /proofs/{id}/revoke` with the `ph_dlg_*` handle. Idempotent by contract, unlike a PROOF, which answers `400 already_revoked`: the delegation contract has always advertised idempotency, while the proof code is published and read by integrators. The body carries the derived `effective_status`/`is_valid` beside the stored `status`, matching the reads

- DelegationListItem: A delegation without its token, carrying stored status plus derived liveness — what the owner listing reports, and what `POST /delegations/{id}/revoke` answers with
  - `id` string, required: MongoDB ObjectId
  - `delegation_id` string, nullable, required: Stable public handle (`ph_dlg_<32 hex>`) — also the token's `sub`, and accepted by the proof status endpoint
  - `principal` string, required: The proven domain that authorized the delegate — derived from the control proof, never client input
  - `delegate` DelegateIdentifier, required: Typed delegate identifier
    - `type` 'url' | 'purl', required: Delegate identifier type: `purl` for a package (`pkg:npm/postmark-mcp`), `url` for an https endpoint
    - `value` string, required: The identifier. Canonicalized per type at creation: purl version/qualifiers/subpath are rejected (delegate the artifact, not a version); urls must be https with no credentials, query or fragment
  - `scope` string[], required
  - `control_proof` string, required: The control proof handle this delegation points down to
  - `status` 'active' | 'revoked', required: The owner's OWN record — whether they revoked this delegation themselves. Nothing else writes it: neither the control proof dying nor this delegation's own lifetime running out. Liveness is DERIVED and reported separately as `effective_status`. This stored field is also what `?status=` filters on
  - `revoked_at` string, date-time, nullable, required
  - `revoked_reason` string, nullable, required
  - `environment` 'production' | 'test', required
  - `issued_at` string, date-time, required
  - `expires_at` string, date-time, required: Never later than the control proof's own expiry at issuance (effective expiry is min of the two)
  - `created_at` string, date-time, required
  - `effective_status` 'active' | 'revoked' | 'expired' | 'suspended', required: DERIVED liveness, computed at read time from BOTH clocks that govern a delegation: the control proof it stands on (cascade rule a — revoked, suspended or expired flows through) and its own `expires_at` (rule b — effective expiry is the earlier of the two). Either one running out reports `expired` here while the delegation's own `status` stays untouched. This is the field to render — `status` alone would show a live badge on a delegation every verifier already rejects
  - `is_valid` boolean, required: True only when `effective_status` is `active`. It is a restatement of that field, not a second opinion: expiry is decided once, inside the derivation above

## Other responses

- `403`: `account_key_required` — this API key is bound to one sub-account; account-level changes need an account-wide key or the dashboard.
- `404`: Delegation not found (uniform for foreign or missing)

---

[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/6b8d61ee354f?raw)
